/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bgu.ppm.sim.impl.mas.agents;

import bgu.ppm.sim.api.mas.MailBox;
import bgu.ppm.sim.api.mas.Message;
import bgu.ppm.sim.api.mas.Model;
import bgu.ppm.sim.api.mas.MultiAgentSystem;
import bgu.sonar.util.reflections.Inject;
import com.esotericsoftware.reflectasm.MethodAccess;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author User
 */
public abstract class MessageDrivenAgent<M extends Model> extends AbstractAgent<M> {

    private MethodAccess access;
    private Map<String, Integer> methodAccessIndex;
    @Inject(type = MultiAgentSystem.class, exceptionIfCannotFindMatch = true)
    private MultiAgentSystem mas;
    private MailBox mailbox;
    private SendMediator sender;
    private boolean started = false;

    @Override
    protected void _initialize(M model) {
        access = MethodAccess.get(getClass());
        methodAccessIndex = new HashMap<>();

        for (Method method : getClass().getMethods()) {
//            System.out.println("Testing method: " + method.getName());
            WhenReceived annotation = method.getAnnotation(WhenReceived.class);
            if (annotation != null) {
                methodAccessIndex.put(annotation.value(), access.getIndex(method.getName(), method.getParameterTypes()));
            }
        }
//        System.out.println("MAS: " + mas);
        mailbox = mas.getMailer().getMailbox(getId());
        sender = new SendMediator(mas.getMailer(), this.getId());
    }

    @Override
    protected void _tick() {
        if (!started) {
            started = true;
            start();
        } else if (mailbox.hasMessages()) {
            Message message = mailbox.popMessage();
            access.invoke(this, methodAccessIndex.get(message.getName()), message.getArguments());
        }
    }

    protected SendMediator send(String name, Object... args) {
        return sender.setMessage(new SimpleMessage(name, args, getId()));
    }

    protected void broadcast(String message, Object... args) {
        for (int i = 0; i < getModel().getNumberOfAgents(); i++) {
            if (i != getId()) {
                send(message, args).to(i);
            }
        }
    }
}
